package org.jeecg.activiti.service.impl;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamReader;

import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.BpmnModel;
import org.activiti.bpmn.model.FlowElement;
import org.activiti.bpmn.model.UserTask;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.repository.ProcessDefinitionQuery;
import org.apache.commons.collections.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.activiti.entity.ActKAppendForm;
import org.jeecg.activiti.entity.ActKAppendFormDeployment;
import org.jeecg.activiti.entity.ActKNode;
import org.jeecg.activiti.entity.ActKNodeDesign;
import org.jeecg.activiti.entity.ActReModelExtend;
import org.jeecg.activiti.entity.ActReModelForm;
import org.jeecg.activiti.entity.ActReModelFormDeployment;
import org.jeecg.activiti.entity.ModelExtend;
import org.jeecg.activiti.mapper.ActivitiModelMapper;
import org.jeecg.activiti.service.IActKAppendFormDeploymentService;
import org.jeecg.activiti.service.IActKAppendFormService;
import org.jeecg.activiti.service.IActKNodeDesignService;
import org.jeecg.activiti.service.IActKNodeService;
import org.jeecg.activiti.service.IActReModelExtendService;
import org.jeecg.activiti.service.IActReModelFormDeploymentService;
import org.jeecg.activiti.service.IActReModelFormService;
import org.jeecg.activiti.service.IActivitiModelService;
import org.jeecg.activiti.util.ActivitiUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.system.vo.LoginUser;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.google.api.client.util.Joiner;


/**
 * @author dousw
 * @version 1.0
 * @date 2020/9/18 14:52
 */
@Service
public class ActivitiModelServiceImpl implements IActivitiModelService {

	@Autowired
	private IActKNodeService iActKNodeService;
	@Autowired
	private IActReModelExtendService iActReModelExtendService;
	@Autowired
	private RepositoryService repositoryService;
	@Autowired
	private IActivitiModelService iActivitiModelService;
	@Autowired
	private IActReModelFormDeploymentService iActReModelFormDeploymentService;
	@Autowired
	private IActReModelFormService iActReModelFormService;
	@Autowired
	private IActKNodeService actKNodeService;
	@Autowired
	private IActKAppendFormService actKAppendFormService;
	@Autowired
	private IActKAppendFormDeploymentService actKAppendFormDeploymentService;
	@Autowired
	private IActKNodeDesignService actKNodeDesignService;
	@Resource
	private ActivitiModelMapper activitiModelMapper;
	

	@SuppressWarnings("unchecked")
	@Override
	@Transactional
	public Result<Object> deploy(String modelId) throws Exception {
		LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
		
		// 获取ModelForm表单
		List<ActReModelForm> actReModelFormList = this.iActReModelFormService.list(new QueryWrapper<ActReModelForm>().eq("model_id", modelId));
		
		// 获取ActReModelExtend
		ActReModelExtend modelExtend = iActReModelExtendService.getOne(new QueryWrapper<ActReModelExtend>().eq("model_id",modelId));
		if(CollectionUtils.isEmpty(actReModelFormList) && "0".equals(modelExtend.getFormType())) {
			return Result.error("请先设计表单并保存");
		}
		// 获取AppendForm表单
		List<ActKAppendForm> actKAppendFormList = this.actKAppendFormService.list(new QueryWrapper<ActKAppendForm>().eq("model_id", modelId));
		
		// 获取模型
		Model modelData = repositoryService.getModel(modelId);
		byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());

		if (bytes == null) {
			return Result.error("模型数据为空，请先成功设计流程并保存");
		}
		InputStream in = new ByteArrayInputStream(bytes);
		XMLInputFactory factory = XMLInputFactory.newFactory();
		XMLStreamReader reader = factory.createXMLStreamReader(in);

		BpmnModel model = new BpmnXMLConverter().convertToBpmnModel(reader);
		
		if (model.getProcesses().size() == 0) {
			return Result.error("模型不符要求，请至少设计一条主线流程");
		}

		// 校验流程图
		Result<Object> validateResult = ActivitiUtils.validateFlowModel(model);
		if (!validateResult.isSuccess()) {
			return Result.error(validateResult.getMessage());
		}

		byte[] bpmnBytes = new BpmnXMLConverter().convertToXML(model);

		// 部署发布模型流程
		String processName = modelData.getName() + ".bpmn20.xml";
		Deployment de = repositoryService.createDeployment().name(modelData.getName()).key(modelData.getKey())
				.addString(processName, new String(bpmnBytes, "UTF-8")).deploy();
		// 获取查询器
		ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery();
		List<ProcessDefinition> list = processDefinitionQuery.deploymentId(de.getId()).list();
		for (ProcessDefinition definition : list) {
			
			ActReModelFormDeployment actReModelFormDeployment = new ActReModelFormDeployment();
			actReModelFormDeployment.setFormType(modelExtend.getFormType());
			if("0".equals(modelExtend.getFormType())){
				// 保存ModelForm表单快照
				ActReModelForm actReModelForm = actReModelFormList.get(0);
				BeanUtils.copyProperties(actReModelForm, actReModelFormDeployment, new String[] { "id", "createBy", "modifyDate", "createTime", "updateBy", "updateTime" });
			}else{
				actReModelFormDeployment.setModelId(modelExtend.getModelId());
				actReModelFormDeployment.setTableName(modelExtend.getTableName());
				actReModelFormDeployment.setFlowStatusCol(modelExtend.getFlowStatusCol());
			}
			actReModelFormDeployment.setCreateBy(sysUser.getUsername());
			actReModelFormDeployment.setCreateTime(Calendar.getInstance().getTime());
			actReModelFormDeployment.setDeploymentId(definition.getDeploymentId());
			actReModelFormDeployment.setVersion(definition.getVersion());
			actReModelFormDeployment.setProcessDefinitionId(definition.getId());
			this.iActReModelFormDeploymentService.save(actReModelFormDeployment);
			
			// 保存AppendForm表单快照
			Result<Object> result = this.actKNodeService.getUserTaskNodeListByModelId(modelId);
			if(!result.getCode().equals(CommonConstant.SC_OK_200)) {
				return result;
			}
			
			// 当前有效的人工节点
			List<ActKNode> userTaskNodeList = (List<ActKNode>) result.getResult();
			if(CollectionUtils.isNotEmpty(userTaskNodeList)) {
				List<String> nodeIdList = userTaskNodeList.stream().map(e -> e.getNodeId()).collect(Collectors.toList());
				List<ActKAppendForm> newActKAppendFormList = actKAppendFormList.stream().filter(item -> nodeIdList.contains(item.getNodeId())).collect(Collectors.toList());
				
				// 添加新设计的流程表单
				if(CollectionUtils.isNotEmpty(newActKAppendFormList)) {
					for(ActKAppendForm actKAppendForm : newActKAppendFormList) {
						ActKAppendFormDeployment actKAppendFormDeployment = new ActKAppendFormDeployment();
						BeanUtils.copyProperties(actKAppendForm, actKAppendFormDeployment, new String[] { "id", "createBy", "modifyDate", "createTime", "updateBy", "updateTime" });
						actKAppendFormDeployment.setCreateBy(sysUser.getUsername());
						actKAppendFormDeployment.setCreateTime(Calendar.getInstance().getTime());
						actKAppendFormDeployment.setDeploymentId(definition.getDeploymentId());
						actKAppendFormDeployment.setVersion(definition.getVersion());
						actKAppendFormDeployment.setProcessDefinitionId(definition.getId());
						this.actKAppendFormDeploymentService.save(actKAppendFormDeployment);
					}
				}
				
			}
			
			ActReModelExtend actReModelExtend = new ActReModelExtend();
			actReModelExtend.setProcessDefinitionId(definition.getId());
			iActReModelExtendService
					.update(new UpdateWrapper<ActReModelExtend>()
							.set("process_definition_id", definition.getId())
							.set("status", 1)
							.set("publish_time", new Date())
							.eq("model_id", modelData.getId())
					);

			iActivitiModelService.handelModelNode(modelId, model, definition);
		}
		return Result.OK("发布成功",null);
	}

	@Override
	public void handelModelNode(String modelId, BpmnModel model, ProcessDefinition definition) {
		// 获取流程节点审批人
		Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
		for (FlowElement e : flowElements) {
			if (e instanceof UserTask) {
				UserTask userTask = (UserTask) e;

				ActKNode actKNode = new ActKNode();
				actKNode.setNodeId(userTask.getId());
				actKNode.setNodeName(userTask.getName());
				actKNode.setAssignee(userTask.getAssignee());
				actKNode.setCandidateUsers(Joiner.on(',').join(userTask.getCandidateUsers()));
				actKNode.setCandidateGroup(Joiner.on(',').join(userTask.getCandidateGroups()));
				actKNode.setIncoming(Joiner.on(',').join(userTask.getIncomingFlows()));
				actKNode.setOutgoing(Joiner.on(',').join(userTask.getOutgoingFlows()));
				actKNode.setModelId(modelId);
				actKNode.setPriority(userTask.getPriority());
				actKNode.setProcessDefinitionId(definition.getId());
				
				ActKNodeDesign actKNodeDesign = this.actKNodeDesignService.getOne(new LambdaQueryWrapper<ActKNodeDesign>()
						.eq(ActKNodeDesign::getModelId, actKNode.getModelId())
						.eq(ActKNodeDesign::getNodeId, actKNode.getNodeId()));
				if(actKNodeDesign != null) {
					actKNode.setNodeType(actKNodeDesign.getNodeType());
				}
				iActKNodeService.save(actKNode);
			}
		}
	}

	@Override
	public List<ModelExtend> getModelListAll(ModelExtend modelExtend) {
		return this.activitiModelMapper.getModelListAll(modelExtend);
	}

	@Override
	public List<Map<String, Object>> selectModelByKey(String key) {
		return this.activitiModelMapper.selectModelByKey(key);
	}

}
